from ..common import *

class BasisAnalysisObj(Enum):
    BASIS_RELATE_PATH = '相对路由'
    BASIS_WHOLE_PATH = '全路由'
    BASIS_BELONG_APP = '归属应用程序'
    BASIS_APP_NAME = '应用程序代码'
    BASIS_URL_ALIAS = '路由别名'
    BASIS_ANALYSIS_NAME = '反向解析名称'

class PathHelper(object):
    def __init__(self, path: Path, app: App=None) -> None:
        super().__init__()
        '''
            一个节点配置一个路径解析包
        '''
        self.path: Path = path # 当前路径解析包
        self.app: App = app # 路径(/节点)归属节点，默认无

    def __repr__(self) -> str:
        return self.path.level # 返回节点深度

class ListTypeCodes(wx.Dialog):

    TEMPLATE_TEMPLATE = "{% url '${name}' ${args}%}"
    TEMPLATE_PYTHON = "django.urls.reverse('${name}', args=(year,))"
    TEMPLATE_REDIRECT = "django.shortcuts.redirect('${name}', foo='bar')"
    
    def __init__(self, parent, select_row):

        wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = 'URL的反向解析（获取网页地址）', size=(500, 150))
        
        '''
            解析数据包
        '''
        code_app_name = select_row["code_app_name"].strip()
        alias = select_row["alias"].strip()
        if "无" == alias:
            alias = ''
        relate_path = select_row["relate_path"].strip()

        r_name = alias
        if code_app_name:
            r_name = f'{code_app_name}:{alias}'

        args = [_[1] for _ in retools.PATT_URL_ARGS.findall(relate_path)]
        r_args = ''
        p_args = ''
        d_args = ''
        if len(args) > 0:
            r_args = ' '.join(args) + ' '
            p_args = ','.join(args) + ','
            d_args = ', ' + ', '.join([f"{arg}={arg}" for arg in args])

        self.template_code = "{% url '" + r_name + "' " + r_args + "%}"
        self.python_code = "django.urls.reverse('" + r_name + "', args=(" + p_args + "))"
        self.redirect_code = "django.shortcuts.redirect('" + r_name + "'" + d_args + ", permanent=False)"

        self._init_UI()

    def _init_UI(self):

        self.panel = wx.Panel(self, wx.ID_ANY)
        self.panelSizer = wx.BoxSizer(wx.VERTICAL)
        self.panel.SetSizer(self.panelSizer)

        self.scollPanel = scrolledpanel.ScrolledPanel(self.panel, -1)
        self.scollPanel.SetupScrolling()
        scollPanelSizer = wx.BoxSizer(wx.VERTICAL)
        self.scollPanel.SetSizer(scollPanelSizer)
        self.panelSizer.Add(self.scollPanel, 3, wx.EXPAND | wx.ALL, 2)

        '''
            自适应网格布局
        '''
        self.flexGridSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5)
        scollPanelSizer.Add(self.flexGridSizer, 1, wx.EXPAND | wx.ALL, 5)
        self.flexGridSizer.AddGrowableCol(1) 

        self.data = [
            (wx.StaticText(self.scollPanel, -1, '模板的反向解析'), wx.TextCtrl(self.scollPanel, -1, value=self.template_code, style=wx.TE_READONLY)),
            (wx.StaticText(self.scollPanel, -1, '代码的反向解析'), wx.TextCtrl(self.scollPanel, -1, value=self.python_code, style=wx.TE_READONLY)),
            (wx.StaticText(self.scollPanel, -1, '重定向反向解析'), wx.TextCtrl(self.scollPanel, -1, value=self.redirect_code, style=wx.TE_READONLY)),
        ]
        
        for _ in self.data:
            self.flexGridSizer.Add(_[0], 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
            self.flexGridSizer.Add(_[1], 0, wx.EXPAND)

class ModifyUrlPathDialog(wx.Dialog):

    def __init__(self, parent, select_row: Dict[str, Any], split_urls: List[str], urls_data: Path, flag: List=None):

        wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = '修改路由路径', size=(400, 300))

        self.flag = flag
        
        '''
            数据包预存
        '''
        self.select_row = select_row
        self.split_urls = split_urls
        self.urls_data = urls_data

        self._init_UI()

    def _init_UI(self):

        self.panel = wx.Panel(self, wx.ID_ANY)
        self.panelSizer = wx.BoxSizer(wx.VERTICAL)
        self.panel.SetSizer(self.panelSizer)

        self.scollPanel = scrolledpanel.ScrolledPanel(self.panel, -1)
        self.scollPanel.SetupScrolling()
        scollPanelSizer = wx.BoxSizer(wx.VERTICAL)
        self.scollPanel.SetSizer(scollPanelSizer)
        self.panelSizer.Add(self.scollPanel, 3, wx.EXPAND | wx.ALL, 2)

        label1 = wx.StaticText(self.scollPanel, -1, '1、【节点】指某些路由的共用节点')
        label2 = wx.StaticText(self.scollPanel, -1, '2、【末级节点】指专属某一路由的节点')
        label3 = wx.StaticText(self.scollPanel, -1, '3、【节点】修改可能会影响共用此节点的其它路由')
        label4 = wx.StaticText(self.scollPanel, -1, '【注意】使用此功能前请确保您的项目已完全使用反向解析！！！')
        label4.SetForegroundColour(wx.RED)
        scollPanelSizer.Add(label1, 0, wx.EXPAND | wx.ALL, 2)
        scollPanelSizer.Add(label2, 0, wx.EXPAND | wx.ALL, 2)
        scollPanelSizer.Add(label3, 0, wx.EXPAND | wx.ALL, 2)
        scollPanelSizer.Add(label4, 0, wx.EXPAND | wx.ALL, 2)

        self.inputFullPath = wx.TextCtrl(self.scollPanel, -1, value=self.select_row["full_path"], style=wx.TE_READONLY)
        scollPanelSizer.Add(self.inputFullPath, 0, wx.EXPAND | wx.ALL, 6)

        '''
            自适应网格布局
        '''
        self.flexGridSizer = wx.FlexGridSizer(cols=3, hgap=5, vgap=5)
        scollPanelSizer.Add(self.flexGridSizer, 1, wx.EXPAND | wx.ALL, 5)
        self.flexGridSizer.AddGrowableCol(1) 

        self.data = []
        
        temp = eval(self.select_row["split_path"])
        for _ in temp[:-1]:
            self.data.append((
                wx.StaticText(self.scollPanel, -1, '节点'),
                wx.TextCtrl(self.scollPanel, -1, value=_),
                wx.StaticText(self.scollPanel, -1, f'其它{self._search_affect_path(_)}个路由受影响'), 
            ))
        if len(temp) > 0: # 末级节点单独处理
            self.data.append((
                wx.StaticText(self.scollPanel, -1, '末级节点'), 
                wx.TextCtrl(self.scollPanel, -1, value=temp[-1]),
                wx.StaticText(self.scollPanel, -1, '其它0个路由受影响'), 
            ))
        
        for _ in self.data:
            self.flexGridSizer.Add(_[0], 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
            self.flexGridSizer.Add(_[1], 0, wx.EXPAND)
            self.flexGridSizer.Add(_[2], 0, wx.EXPAND)

        '''
            预览和修改
        '''
        self.panelToolsPanel = wx.Panel(self.scollPanel, wx.ID_ANY)
        self.panelToolsPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.panelToolsPanel.SetSizer(self.panelToolsPanelSizer)
        scollPanelSizer.Add(self.panelToolsPanel, 0, wx.EXPAND | wx.ALL, 2)

        self.btnPreview = buttons.GenButton(self.panelToolsPanel, -1, '预览')
        self.btnPreview.SetBackgroundColour(CON_COLOR_MAIN)
        self.btnPreview.SetForegroundColour(CON_COLOR_PURE_WHITE)
        self.panelToolsPanelSizer.Add(self.btnPreview, 1, wx.EXPAND | wx.ALL, 5)

        self.btnModify = buttons.GenButton(self.panelToolsPanel, -1, '修改')
        self.btnModify.SetBackgroundColour(CON_COLOR_MAIN)
        self.btnModify.SetForegroundColour(CON_COLOR_PURE_WHITE)
        self.panelToolsPanelSizer.Add(self.btnModify, 1, wx.EXPAND | wx.ALL, 5)

        self.Bind(wx.EVT_BUTTON, self.onBtnPreview, self.btnPreview)
        self.Bind(wx.EVT_BUTTON, self.onBtnModify, self.btnModify)

    def _search_affect_path(self, node_name):
        """寻找受影响的路由数"""
        obj: List[str] = eval(self.select_row["split_path"])
        index = obj.index(node_name)
        cut_obj = obj[:index+1]
        '''
            解析原理：
                假设要修改的拆分路由url1为['a/', 'b/', 'c/']
                另一个路由url2为['a/', 'b/', 'd/']
            无非下面几种情形：
                1、url2 的级数恰好等于或深于 url1
                2、url2 的级数浅于 url1
            当 url1 修改 'b/' 节点时，url2的情况类似于上面的情况。
        '''
        affect_count = 0
        for _ in self.split_urls:
            if len(_) < index + 1: # 当前路由总级数达不到共用节点的要求
                continue
            if cut_obj == _[:index+1]:
                affect_count += 1
        return affect_count - 1 # 排除自身

    def onBtnModify(self, evt):
        '''点击修改'''
        if RichMsgDialog.showYesNoMsgDialog(self, '修改有风险，若您的项目未完全采用反向解析，请确保本路由以及涉事路由已采用反向解析，否则不建议修改。', '提示'):
            nodes: List[str] = eval(self.select_row["split_path"]) # 待修改的拆分路由
            modify_nodes = [_[1].GetValue().strip(string.whitespace+'/')+'/' for _ in self.data] # 输入框的内容
            '''
                原则上，只要是界面能够展示的路由均能够准确定位修改【极方便地实现】
            '''
            node_path_children = self.urls_data.children
            success = 0
            for i in range(len(nodes)): # 总共需修改的次数
                '''
                    当前路由节点如果是【节点】，则需要加强节点的判断
                    如果是【末级节点】，则仅需要找到即可
                    （对于不标准情况，若两个路由共用同一个链接，这本身就不合法。本程序的处理会比较随机【取优先找到的节点修改】）

                    此处的 i 拥有另一层含义：路由级数
                '''
                temp_path = None # 一个级数 对应 该级数的查找
                for child_path in node_path_children:
                    if child_path.split_path == nodes[:i+1]: # 定位节点
                        temp_path = child_path
                        break
                if temp_path is None:
                    RichMsgDialog.showOkMsgDialog(self, '路由数据被破坏，修改失败', '错误')
                    return

                '''
                    如果 本节点内容为改变，则跳过不改
                '''
                node_path_children = temp_path.children # 寻找到下一级数
                if nodes[i] == modify_nodes[i]:
                    success += 1
                    continue

                '''
                    节点修改功能实现【依赖于路由的解析引擎，准度的高低取决于解析引擎的强大程度】
                '''
                file_path = temp_path.node_file if temp_path.isApp else temp_path.app_file
                url_patterns = djangotools.get_list_patt_content(retools.PATT_URLPATTERNS, file_path)
                deal_path = temp_path.node_str if temp_path.isNode else temp_path.origin_str
                modify_node_str = deal_path.replace(nodes[i], modify_nodes[i]) # 提高容错率
                modify_url_patterns = url_patterns.replace(deal_path, modify_node_str) # 提高容错率
                ### 写入
                write_file(file_path, read_file(file_path).replace(url_patterns, modify_url_patterns))
                success += 1
                
            if success == len(modify_nodes):
                if self.flag is not None:
                    self.flag.append(0)
                RichMsgDialog.showOkMsgDialog(self, '修改成功！！！', '提示')
            else:
                RichMsgDialog.showOkMsgDialog(self, f'修改失败，共{len(modify_nodes)}个节点，仅成功修改{success}个', '提示')
            self.Close()
        else:
            RichMsgDialog.showOkMsgDialog(self, '已取消', '提示')

    def onBtnPreview(self, evt):
        '''预览
        
            处理原则：100%不相信用户的输入。
        '''
        temp_path: str = self.select_row["full_path"]
        temp_relate_path: str = self.select_row["relate_path"]
        modify_nodes = [_[1].GetValue().strip(string.whitespace+'/')+'/' for _ in self.data]
        RichMsgDialog.showScrolledMsgDialog(
            self, 
            temp_path.replace(temp_relate_path, '').strip(string.whitespace+'/')+'/' + ''.join(modify_nodes), 
            '路由修改界面预览'
        )
